/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package uk.ac.rdg.resc.mmd.controllers;

import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import org.joda.time.DateTime;

/**
 *
 * @author Alastair
 */
public class DepthTimePlotController {

    public static void getFilledBlockPlot(String deepDepthStr, List<Double> elevations,
            List<List<Float>> profileTimeseriesValues, List<DateTime> dates, OutputStream outputStream) throws IOException {

        final double deepestDepth = deepDepthStr.equals("") ? elevations.get(elevations.size() - 1) :
            Double.parseDouble(deepDepthStr);
        final int nDepthBins = (int)Math.abs(deepestDepth) / 10;
        final int depthIncr = (int)Math.abs(deepestDepth / nDepthBins);

        int[] depthIndices = getDepthIndices(elevations, nDepthBins, depthIncr);

        double[][] chartData = prepareFilledBlockPlot(profileTimeseriesValues, dates, depthIndices, deepestDepth);

        JFreeChartController.streamFilledBlockChart(outputStream, chartData, depthIncr);
    }

    public static void getContourPlot(String deepDepthStr, List<Double> elevations,
            List<List<Float>> profileTimeseriesValues, List<DateTime> dates, OutputStream outputStream) throws IOException {

        final double deepestDepth = deepDepthStr.equals("") ? elevations.get(elevations.size() - 1) :
            Double.parseDouble(deepDepthStr);

        Double[][] chartData = prepareContourPlot(profileTimeseriesValues, elevations, dates, deepestDepth);

        JFreeChartController.streamContourPlot(outputStream, chartData, deepestDepth);
    }

    private static int[] getDepthIndices(List<Double> elevations, int nDepthBins, int depthIncr) {
        int[] depthIndices = new int[nDepthBins];
        int currentModelIndex = 0;
        for (int iDepth = 0; iDepth < nDepthBins; iDepth++) {
            double depth = iDepth * depthIncr * -1;
            for (int iModDepth = currentModelIndex; iModDepth < elevations.size(); iModDepth++) {
                double modelDepth = elevations.get(iModDepth);
                double shallowDepth = iModDepth == 0 ? 0 : elevations.get(iModDepth - 1);
                double deepDif = depth - modelDepth;
                double shallowDif = depth - shallowDepth;
                int index = 0;
                if (deepDif == 0) {
                    index = iModDepth;
                } else if (shallowDif == 0) {
                    index = iModDepth - 1;
                } else if (Math.signum(deepDif) != Math.signum(shallowDif)) {
                    double absDeepDif = Math.abs(Math.abs(depth) - Math.abs(modelDepth));
                    double absShallowDif = Math.abs(Math.abs(depth) - Math.abs(shallowDepth));
                    index = absDeepDif <= absShallowDif ? iModDepth : iModDepth - 1;
                } else {
                    // try again with next iModDepth
                    continue;
                }
                if (index == -1) index = 0;
                System.out.println("depth: " + depth + ", modelDepth: " + modelDepth + ", shallowDepth: " + shallowDepth
                        + ", deepDif: " + deepDif + ", shallowDif: " + shallowDif + ", index: " + index);
                depthIndices[iDepth] = index;
                currentModelIndex = index;
                break;
            }
        }
        return depthIndices;
    }

    private static double[][] prepareFilledBlockPlot(List<List<Float>> profileTimeseriesValues,
            List<DateTime> dates, int[] depthIndices, double deepestDepth) {

        final int nDepthBins = (int)Math.abs(deepestDepth) / 10;
        final int depthIncr = (int)Math.abs(deepestDepth / nDepthBins);

        int nDataPoints = profileTimeseriesValues.size() * nDepthBins;
        double[] depths = new double[nDataPoints];
        double[] times = new double[nDataPoints];
        double[] values = new double[nDataPoints];

        int firstCumlMonth = dates.get(0).getYear() * 12 + dates.get(0).getMonthOfYear();
        int count = 0;
        for (int iTime = 0; iTime < profileTimeseriesValues.size(); iTime++) {
            for (int iDepth = 0; iDepth < nDepthBins; iDepth++) {
                int cumlMonth = dates.get(iTime).getMonthOfYear() + (dates.get(iTime).getYear() * 12);
                int normMonth = cumlMonth - firstCumlMonth;
                times[count] = new Double(normMonth);

                double depth = iDepth * depthIncr * -1;
                if (depth < deepestDepth) {
                    depths[count] = depth;
                    values[count] = Double.NaN;
                }
                else {
                    depths[count] = depth;
                    values[count] = profileTimeseriesValues.get(iTime).get(depthIndices[iDepth]) == null ? Double.NaN : new Double(profileTimeseriesValues.get(iTime).get(depthIndices[iDepth]));
                }
                count++;
            }
        }
        double[][] data = new double[][]{times, depths, values};
        return data;
    }

    private static Double[][] prepareContourPlot(List<List<Float>> profileTimeseriesValues, List<Double> elevations,
            List<DateTime> dates, double deepestDepth) {
        int nDataPoints = profileTimeseriesValues.size() * profileTimeseriesValues.get(0).size();
        Double[] depths = new Double[nDataPoints];
        Double[] times = new Double[nDataPoints];
        Double[] values = new Double[nDataPoints];

        int firstCumlMonth = dates.get(0).getYear() * 12 + dates.get(0).getMonthOfYear();
        int count = 0;
        for (int iTime = 0; iTime < profileTimeseriesValues.size(); iTime++) {
            for (int iDepth = 0; iDepth < profileTimeseriesValues.get(0).size(); iDepth++) {
                int cumlMonth = dates.get(iTime).getMonthOfYear() + (dates.get(iTime).getYear() * 12);
                int normMonth = cumlMonth - firstCumlMonth;
                times[count] = new Double(normMonth);

                double depth = elevations.get(iDepth);
                if (depth < deepestDepth) {
                    depths[count] = elevations.get(iDepth);
                    values[count] = null;
                }
                else {
                    depths[count] = elevations.get(iDepth);
                    values[count] = profileTimeseriesValues.get(iTime).get(iDepth) == null ? null : new Double(profileTimeseriesValues.get(iTime).get(iDepth));
                    //System.out.println("x/y/z: " + times[count].doubleValue() + "/" + depths[count].doubleValue() + "/" + values[count].doubleValue());
                }
                count++;
            }
        }
        return new Double[][]{times, depths, values};
    }
}
